<?xml version="1.0" encoding="UTF-8"?>
<doc>
<title>类(class)</title>

<para>
<ptitle>类(class)</ptitle>
　“类”是创建“对象”时使用的“模型”。类中的变量称为“成员变量”、类中的函数称为“成员函数”、或者叫做“方法”、类中的属性称为“成员属性”。<r/>
<r/>
　类的声明就象下面这样。<r/>
<bq>
<kw>class</kw> classname<r/>
{<r/>
	// 定义成员变量，成员函数和成员属性<r/>
<r/>
	function classname() // 构造函数<r/>
	{<r/>
	}<r/>
<r/>
	function <kw>finalize</kw>() // 这个相当于析构函数<r/>
	{<r/>
	}<r/>
}<r/>
<r/>
</bq>
<r/>
　指定一个标识符作为类名。<r/>
<r/>
　在类的内部定义变量、函数和属性，作为类的成员。<r/>
<r/>
　在类的定义中必须有一个和类同名的成员函数，这个成员函数被称为“<kw>构造函数</kw>”。使用<kw>new</kw> 运算符创建这个类的对象的时候，这个函数就会作为 new 运算符的参数而被调用。<r/>
<r/>
　在类的定义中同样存在一个名为“finalize”的特殊的成员函数。这个函数会在对象销毁时被调用、可以省略。就算不写也没关系(如果想在对象销毁时做一些自己想做的事，就可以自己定义这个函数，把想要进行的操作写进去)。<r/>
<r/>
<example>
	class test<r/>
	{<r/>
		function test()<r/>
		{<r/>
			// 构造函数<r/>
			// 在这里填写对象创建时需要处理的内容。<r/>
			variable = 0;<r/>
		}<r/>
<r/>
		function finalize()<r/>
		{<r/>
			// finalize 方法<r/>
			// 在这里填写对象销毁时需要处理的内容。<r/>
		}<r/>
<r/>
		function method1()<r/>
		{<r/>
			// 方法<r/>
			System.inform(variable);<r/>
		}<r/>
<r/>
		var variable; // 成员变量<r/>
<r/>
		property prop // 成员属性<r/>
		{<r/>
			getter() { return variable; } <r/>
		}<r/>
	}<r/>
</example>
<r/>
对类使用 instanceof 运算符和 "Class" 操作数时，会得到“真”(true)的结果(以上面的那个类为例、test instanceof "Class" 是“真”)。<r/>

</para>

<para>
<ptitle>对象的创建</ptitle>
　已定义的类的对象使用 new 运算符来创建。<r/>
　new 运算符的后面像函数那样指定类名和传递给构造函数的参数。<r/>
<example>
	class Test<r/>
	{<r/>
		var variable1 = getValue(); // 成员变量的初始化<r/>
<r/>
		function Test(arg1, arg2) // 构造函数<r/>
		{<r/>
			// 使用 new 运算符创建对象时指定的参数会被传到 arg1 和 arg2 这两个变量里<r/>
		}<r/>
	}<r/>
<r/>
	var newobject = new Test(1, 2); // 把 1 和 2 作为参数传入，创建 test 类的对象<r/>
</example>
<r/>
　对象在创建时的处理顺序如下。<r/>
<r/>
<ol>
<li>首先创建一个空的对象</li>
<li>注册方法和属性</li>
<li>创建成员函数 ( 有必要初始化的变量会在这时初始化 )</li>
<li>执行构造函数</li>
</ol>

<note>
　即使在构造函数不需要参数的时候也不能省略 new 运算符和类名后面的  ( ) 。像 JavaScript 那样写成 new Test 是不行的。必须写成 new Test( ) 。<r/>
</note>

<r/>
　注意，在类的方法和属性里创建该类的对象或该类的超类的对象的时候，象下面这样写是错误的。<r/>
<example>
	class Test<r/>
	{<r/>
		function Test() // 构造函数<r/>
		{<r/>
		}<r/>
<r/>
		function func()<r/>
		{<r/>
			return new Test(); // 错误<r/>
		}<r/>
	}<r/>
</example>
<r/>
　其原因在于，在类的方法或属性里只写 Test 的话、因为相对于类名的 Test 构造函数的 Test 在范围上更接近，这个 Test 会被当作构造函数来处理。为了避免这种情况，应该像下面这样明确地使用 global. 来指定类名 ( 因为类已经注册到 global 里了 )。<r/>
<r/>
<example>
	class Test<r/>
	{<r/>
		function Test() // 构造函数<r/>
		{<r/>
		}<r/>
<r/>
		function func()<r/>
		{<r/>
			return new global.Test(); // 这样写就OK了<r/>
		}<r/>
	}<r/>
</example>
</para>

<para>
<ptitle>对象的销毁</ptitle>
　在 TJS2 语言中，销毁对象时分为对象的<kw>无效化</kw>和对象的销毁两个步骤。<r/>
　对象被无效化的时候会调用 finalize 方法，然后该对象会被置上无效的标记。以后对该对象的所有操作都会失败并产生异常。可以通过调用 <kw>isvalid</kw> 运算符来判断一个对象是否已经失效。<r/>
<r/>
　使用 <kw>invalidate</kw> 运算符来对对象进行无效化操作。<r/>
<r/>
<example>
	class Test<r/>
	{<r/>
		var variable;<r/>
<r/>
		function Test()<r/>
		{<r/>
			// 构造函数<r/>
			variable = new AnotherClass();<r/>
		}<r/>
<r/>
		function finalize()<r/>
		{<r/>
			// finalize 方法会在对象被无效化的时候会调用<r/>
			invalidate variable;<r/>
		}<r/>
	}<r/>
<r/>
	var object = new Test(); // 对象的创建<r/>
<r/>
	(略)<r/>
<r/>
	invalidate object; // 对象的无效化<r/>
</example>
<r/>
　即使不使用 invalidate 运算符来操作，对象也会在没用了的时候被删除。没有被无效化的对象在这时候会被无效化。<r/>
　TJS2中并没有对“什么时候对象会被销毁”进行明确规定，销毁和无效化“随时可能发生”。未无效化的对象在被销毁时会先被无效化，此时可能会自动调用 finalize 方法。为了避免这一点，建议在对象用完之后使用 invalidate 运算符对其进行无效化。<r/>
<r/>
<note>
　invalidate 运算符的行为类似于 C++ 中的 delete 运算符。<r/>
　TJS2 中的 delete 运算符和 C++ 中的 delete 运算符是不一样的，TJS2 中的 delete 是用来消除成员或全局变量的运算符。虽然 delete 运算符无法直接对对象进行无效化或删除，但是成员或全局变量的删除会引起其内部对象的无效化和删除。<r/>
</note>
</para>

<para>
<ptitle>对对象的操作</ptitle>
　已创建的对象的成员变量、方法和成员属性可以通过 <kw>.</kw> (成员选择运算符) 或者 <kw>[ ]</kw> (间接成员选择运算符) 来操作。<r/>
<example>
	var obj=new MyLayer(window,window.prmaryLayer)<r/>
	obj.method1(); // 方法的调用 或者写成 obj['method1']() 也可以<r/>
	obj.num = 3; // 成员变量的赋值 或者写成 obj['num'] = 3 也可以<r/>
	obj.prop1++; // 成员属性的自增操作 或者写成 obj['prop1']++ 也可以<r/>
</example>
</para>

<para>
<ptitle>闭包(Closure)</ptitle>
　已创建的对象的方法和属性是携带着“属于哪个对象”的信息被注册到对象中的。<r/>
　因此，这些方法和属性从对象中取出，单独使用时，仍是对其所属对象的操作。这一机能被称为闭包(Closure)。那个被操作的对象称为<kw>上下文(context)</kw>。<r/>
<example>
	var obj = new FooBarClass(); // 创建对象<r/>
	obj.method(); // 用常规手段调用对象的方法<r/>
	var objmethod = obj.method; // 把指向对象中方法的引用代入到 objmethod 变量<r/>
	objmethod(); // 调用 objmethod 和调用 obj.method() 一样，都是对 obj 这个对象的操作<r/>
</example>
<r/>
　<kw>incontextof</kw> 运算符提供了修改函数中“作为哪个对象的方法”这一信息，让某个函数作为任意对象中的方法来调用的功能。<r/>
<example>
	(objmethod incontextof obj2)(); // 对 obj2 进行操作<r/>
	(objmethod incontextof this)(); // 对 this 进行操作
</example>

</para>


<para>
<ptitle>继承</ptitle>
　使用关键字 <kw>extends</kw> 可以让一个类继承其他的类。继承操作中，源类的成员都会被目的类继承下来。<r/>
　被继承的类称之为<kw>超类(super class)</kw>，继承出来的类称之为<kw>子类(sub class)</kw>。<r/>
　以如下方式定义类。<r/>
<example>
	class Class1 // 超类<r/>
	{<r/>
		function Class1() // Class1 的构造函数<r/>
		{<r/>
		}<r/>
<r/>
		function finalize() // Class1 finalize<r/>
		{<r/>
		}<r/>
<r/>
		function method1() // method1<r/>
		{<r/>
		}<r/>
	}<r/>
<r/>
<r/>
	class Class2 extends Class1<r/>
	{<r/>
		function Class2() // Class2 构造函数<r/>
		{<r/>
			super.Class1(); // 调用 Class1 的构造函数<r/>
		}<r/>
<r/>
		function finalize() // Class2 finalize<r/>
		{<r/>
			super.finalize();<r/>
		}<r/>
	}<r/>
<r/>
	var obj = new Class2(); // 创建 Class2 的对象<r/>
	obj.method1(); // Class2 调用了从 Class1 中继承的 method1 方法<r/>
</example>
　上在上面的例子中、Class2 继承了 Class1 。或者称为 Class2 由 Class1 <kw>派生</kw> 而来。<r/>
　Class2 的构造函数内调用了 Class1 的构造函数，Class2 的 finalize 内调用了 Class1 的 finalize 。<b>子类的这些方法中没有调用超类的对应的方法的时候，其结果是不可预知的</b> ( 现在的版本中并没有对是否调用进行检测 ) ，所以请务必手动调用超类对应的函数。<r/>
<r/>
　为了在子类中调用超类的成员，可以使用 <kw>super</kw> 运算符。这个关键字在子类中才能使用，代表所在类的超类。<r/>
<r/>
　在进行了继承的情况下，用 new 运算符创建对象时的初始化顺序如下。<r/>
<r/>
<ol>
<li>首先创建空的对象</li>
<li>注册方法和属性( 从超类的方法和属性开始，然后才是子类的 )</li>
<li>创建成员变量 ( 从超类的成员变量开始，然后才是子类的 )</li>
<li>调用子类的构造函数</li>
<li>( 从子类的构造函数里 ) 调用超类的构造函数</li>
</ol>
</para>

<para>
<ptitle>多重继承</ptitle>
　在 extends 后面填写多个超类的类名，就可以进行多重继承。<r/>
<example>
	class SubClass extends ClassA, ClassB<r/>
	{<r/>
		function SubClass() // SubClass 构造函数<r/>
		{<r/>
			ClassA(); // 调用 ClassA 的构造函数<r/>
			ClassB(); // 调用 ClassB 的构造函数<r/>
		}<r/>
<r/>
		function finalize() // Class2 finalize<r/>
		{<r/>
			global.ClassA.finalize();<r/>
			global.ClassB.finalize();<r/>
		}<r/>
	}<r/>
<r/>
</example>
　在这里不能使用 super 关键字，必须明确指定超类的类名。调用超类的方法时用 global. 后面跟各个类名来对类进行操作，例如在子类中单独指定 ClassA 及其构造函数。因为类本身已经在 global 中注册了，所以使用 global. 可以对类进行操作。<r/>
<r/>
　进行多重继承的情况下，用 new 运算符创建对象时的初始化顺序和非多重继承的情况相同，超类的方法和属性的注册顺序依照 extends 关键字后面的类名的顺序。如果被继承的类之间有同名的方法或属性的话，写在后面的类优先。被隐藏的方法和属性可以通过明确指定类名，用 global.ClassA.hiddenMethod() 这样的方式来调用。<r/>
</para>

<para>
<ptitle>override</ptitle>
　译者注：“オーバーライド”貌似应该翻译成 “override”，这在中文的C++相关资料中被称为“覆盖”。但是，在TJS2中没有虚函数和晚捆绑机制以及多态性，所以，以下将这一现象称为“隐藏”。感谢Bruce Eckel和他的《Thinking in C++》。<r/>
<r/>
　子类中定义了和超类中同名的方法和属性时，超类的成员会被隐藏。这被称为“override”。<r/>
　上面那段说明中子类的 finalize 方法就把超类的 finalize 方法隐藏了。<r/>
<example>
	class Class1 // 超类<r/>
	{<r/>
		function Class1() // Class1 构造函数<r/>
		{<r/>
		}<r/>
<r/>
		function finalize() // Class1 finalize<r/>
		{<r/>
		}<r/>
<r/>
		function method1() // method1<r/>
		{<r/>
			(略)<r/>
		}<r/>
	}<r/>
<r/>
<r/>
	class Class2 extends Class1<r/>
	{<r/>
		function Class2() // Class2 构造函数<r/>
		{<r/>
			super.Class1(); // 调用 Class1 的构造函数<r/>
		}<r/>
<r/>
		function finalize() // Class2 finalize<r/>
		{<r/>
			super.finalize();<r/>
		}<r/>
<r/>
		function method1() // 隐藏 Class1.method1 <r/>
		{<r/>
			(略)<r/>
			if(略) return super.method1();<r/>
			(略)<r/>
		}<r/>
	}<r/>
<r/>
	var obj = new Class2(); // 创建 Class2 对象<r/>
	obj.method1(); // 调用 Class2 的 method1 方法<r/>
</example>
　在子类的方法或属性中可以用 super 关键字来调用超类的方法或属性。<r/>
<r/>
　成员变量无法隐藏。成员变量是针对某一个对象进行注册的，子类可以和超类有相同名字的成员函数
但子类的成员变量会把超类的成员变量覆盖掉。<r/>
</para>

</doc>